﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using ESpace;

namespace Organizer
{
    public partial class FormSticker : Form
    {
        #region *** функции скрытия по alt+tub и отображения позади всех окон ******************

        #region *** скрытие по alt+tub ******************
        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr window, int index, int value);

        [DllImport("user32.dll")]
        private static extern int GetWindowLong(IntPtr window, int index);

        private const int GWL_EXSTYLE = -20;
        private const int WS_EX_TOOLWINDOW = 0x00000080;

        public static void HideFromAltTab(IntPtr Handle)
        {
            SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
                GWL_EXSTYLE) | WS_EX_TOOLWINDOW);
        }
        #endregion *** скрытие по alt+tub ******************

        #region *** функции отображения позади всех окон ******************
        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(int hWnd, int hWndInsertAfter, int X, int Y,
            int cx, int cy, uint uFlags);

        public const int HWND_BOTTOM = 0x1;
        public const uint SWP_NOSIZE = 0x1;
        public const uint SWP_NOMOVE = 0x2;
        public const uint SWP_SHOWWINDOW = 0x40;

        private void ShoveToBackground()
        {
            SetWindowPos((int)this.Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE |
                SWP_NOSIZE | SWP_SHOWWINDOW);
        }
        #endregion *** функции отображения позади всех окон ******************

        #endregion *** функции скрытия по alt+tub и отображения позади всех окон ******************

        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            HideFromAltTab(this.Handle);
            if (!bMenu) ShoveToBackground();
            txbText.Select(txbText.Text.Length, 0);
        }

        static Color[] clr = new Color[6]
        {
            Color.FromArgb(255, 255, 204), // желтый
            Color.FromArgb(204, 255, 204), // зеленый
            Color.FromArgb(204, 247, 255), // голубой
            Color.FromArgb(213, 204, 255), // сиреневый
            Color.FromArgb(255, 204, 213), // розовый
            Color.FromArgb(240, 240, 240) // белый
        };

        public int colorInd = 0;

        DateTime nextSave = DateTime.MaxValue; // время следующего сохранения

        public delegate void ChangeDelegate(Sticker.ChangeAction actn, object prm);
        public ChangeDelegate Change;

        int TitleHeight = 42;

        Sticker stk;

        public string ContentText
        {
            get
            {
                return txbText.Text;
            }
            set
            {
                bSetText = true;
                txbText.Text = value;
                AdaptContestBySize(false);
                bSetText = false;
            }
        }

        bool bMenu = false;

        public FormSticker(Sticker stk)
        {
            InitializeComponent();
            this.stk = stk;
        }

        public void Construct(NetStatus stat)
        {
            SetColor();
            lblHide.Font = txbText.Font;

            // размер
            AdaptContestBySize(false);

            Bitmap bmp = new Bitmap(16, 16);
            Graphics gr = Graphics.FromImage(bmp);
            ToolStripMenuItem itm;
            gr.Clear(clr[0]); itm = new ToolStripMenuItem("Желтый", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_0"; menuMain.Items.Add(itm);
            gr.Clear(clr[1]); itm = new ToolStripMenuItem("Зеленый", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_1"; menuMain.Items.Add(itm);
            gr.Clear(clr[2]); itm = new ToolStripMenuItem("Голубой", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_2"; menuMain.Items.Add(itm);
            gr.Clear(clr[3]); itm = new ToolStripMenuItem("Сиреневый", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_3"; menuMain.Items.Add(itm);
            gr.Clear(clr[4]); itm = new ToolStripMenuItem("Розовый", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_4"; menuMain.Items.Add(itm);
            gr.Clear(clr[5]); itm = new ToolStripMenuItem("Белый", (Bitmap)bmp.Clone(), new EventHandler(mBtn_Click)); itm.Name = "itm_5"; menuMain.Items.Add(itm);
            menuMain.Items.Add(new ToolStripSeparator());
            //itm = new ToolStripMenuItem("Скрыть стикер в заметку"); itm.Click += new EventHandler(mBtnToNote_Click); menuMain.Items.Add(itm);

            itm = new ToolStripMenuItem("Сделать стикер сетевым"); itm.Click += new EventHandler(mBtnToNet_Click); itm.Visible = stat == NetStatus.Local; menuMain.Items.Add(itm);
            itm = new ToolStripMenuItem("Сделать стикер локальным"); itm.Click += new EventHandler(mBtnToLocal_Click); itm.Visible = stat != NetStatus.Local; menuMain.Items.Add(itm);

            menuMain.Items.Add(itm);

            gr.Dispose();
            bmp.Dispose();

            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            imResize.BackColor = Color.Transparent;
            UpdateNetStat(stat);
        }

        public void SetColor()
        {
            txbText.BackColor = clr[colorInd];
            pnlTitle.BackColor = Color.FromArgb(clr[colorInd].R - 20, clr[colorInd].G - 20, clr[colorInd].B - 20);
            BackColor = clr[colorInd];
        }

        public void mBtn_Click(object Sender, EventArgs e)
        {
            colorInd = Convert.ToInt32(((ToolStripMenuItem)Sender).Name.Split('_')[1]);
            SetColor();
            Change(Sticker.ChangeAction.ColorChange, null);
            OnActivated(null);
        }

        public void mBtnToNote_Click(object Sender, EventArgs e)
        {
            Change(Sticker.ChangeAction.ConvertToNote, null);
        }

        public void mBtnToNet_Click(object Sender, EventArgs e)
        {
            Change(Sticker.ChangeAction.SetNet, null);
        }

        public void mBtnToLocal_Click(object Sender, EventArgs e)
        {
            Change(Sticker.ChangeAction.SetLocal, null);
        }

        private void menuMain_Opening(object sender, CancelEventArgs e)
        {
            btnAdd.Visible = false;
            btnDel.Visible = false;
        }

        private void menuMain_Opened(object sender, EventArgs e)
        {
            bMenu = true;
            OnActivated(null);
            bMenu = false;
            Gl.AddDebugLog("Открылось меню стикера '" + stk.Name + "'", false);
        }

        Point pDownM = new Point(int.MinValue, int.MinValue);
        Point pDownFrm = new Point(int.MinValue, int.MinValue);
        private void pnlTitle_MouseDown(object sender, MouseEventArgs e)
        {
            pDownM = Cursor.Position;
            pDownFrm = new Point(Left, Top);
        }

        private void pnlTitle_MouseUp(object sender, MouseEventArgs e)
        {
            pDownM = new Point(int.MinValue, int.MinValue);

            Rectangle disp = Screen.PrimaryScreen.WorkingArea;

            if (Bottom > disp.Bottom) Top = disp.Bottom - Height;
            if (Right > disp.Right) Left = disp.Right - Width;
            if (Top < disp.Top) Top = 0;
            if (Left < disp.Top) Left = 0;

            // если было движение
            if (Location != pDownFrm)
                Change(Sticker.ChangeAction.Move, pDownFrm);
        }

        private void pnlTitle_MouseMove(object sender, MouseEventArgs e)
        {
            if (pDownM.X != int.MinValue)
            {
                Top = pDownFrm.Y - (pDownM.Y - Cursor.Position.Y);
                Left = pDownFrm.X - (pDownM.X - Cursor.Position.X);
                /*if (((StickerUnit)stk.Glob).IsFit(stk, Location, Size))
                    Cursor.Current = Cursors.Default;
                else
                    Cursor.Current = Cursors.No;*/
            }
        }

        private void pnlTitle_MouseLeave(object sender, EventArgs e)
        {
            Rectangle rect1 = btnAdd.RectangleToScreen(btnAdd.ClientRectangle);
            Rectangle rect2 = btnDel.RectangleToScreen(btnDel.ClientRectangle);
            if ((Cursor.Position.X <= rect1.Left || Cursor.Position.Y <= rect1.Top || Cursor.Position.X >= rect1.Right || Cursor.Position.Y >= rect1.Bottom) &&
                (Cursor.Position.X <= rect2.Left || Cursor.Position.Y <= rect2.Top || Cursor.Position.X >= rect2.Right || Cursor.Position.Y >= rect2.Bottom))
            {
                btnAdd.Visible = false;
                btnDel.Visible = false;
            }
        }

        private void pnlTitle_MouseEnter(object sender, EventArgs e)
        {
            btnAdd.Visible = true;
            btnDel.Visible = true;

        }

        private void btnAdd_Enter(object sender, EventArgs e)
        {
            btnAdd.BackColor = Color.FromArgb(70, 255, 255, 255);
        }

        private void btnAdd_Leave(object sender, EventArgs e)
        {
            btnAdd.BackColor = Color.Transparent;
            btnAdd.Visible = false;
            btnDel.Visible = false;
        }

        private void btnDel_MouseEnter(object sender, EventArgs e)
        {
            btnDel.BackColor = Color.FromArgb(70, 255, 255, 255);
        }

        private void btnDel_MouseLeave(object sender, EventArgs e)
        {
            btnDel.BackColor = Color.Transparent;
            btnAdd.Visible = false;
            btnDel.Visible = false;
        }

        private void tmrMain_Tick(object sender, EventArgs e)
        {
            //label1.Text = txbText.Lines.Length.ToString();
        }

        bool bSetText = false;
        private void txbText_TextChanged(object sender, EventArgs e)
        {
            if (!bSetText)
            {
                AdaptContestBySize(true);
                Change(Sticker.ChangeAction.Edit, null);
            }
        }

        /// <summary>
        /// Подгонка контента, под размеры окна стикера
        /// </summary>
        /// <param name="AutoSize">автоматически увеличивать размер по вертикали, если текст не умещается и нет скролбара</param>
        public void AdaptContestBySize(bool AutoSize)
        {
            lblHide.MaximumSize = new Size(txbText.ClientSize.Width, int.MaxValue);
            lblHide.Text = txbText.Text;
            if (txbText.Text.Length > 0) if (txbText.Text[txbText.Text.Length - 1] == '\n') lblHide.Text += " ";
            Size sz = lblHide.Size;

            if (sz.Height > txbText.Height)
            {
                if (AutoSize && txbText.ScrollBars == ScrollBars.None)
                {
                    if (!SetNewSize(Width, sz.Height + TitleHeight))
                    {
                        txbText.ScrollBars = ScrollBars.Both;
                        txbText.ScrollToCaret();
                    }
                }
                else
                {
                    txbText.ScrollBars = ScrollBars.Both;
                    txbText.ScrollToCaret();
                }
            }
            else
            {
                txbText.ScrollBars = ScrollBars.None;
            }

            if (BackgroundImage != null) BackgroundImage.Dispose();
            BackgroundImage = new Bitmap(Width, Height);
            Graphics gr = Graphics.FromImage(BackgroundImage);
            gr.DrawRectangle(Pens.Gray, 0, 0, Width - 1, Height - 1);
            Refresh();
        }

        private void FormSticker_FormClosing(object sender, FormClosingEventArgs e)
        {
            //
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            Change(Sticker.ChangeAction.Add, colorInd);
        }

        private void btnDel_Click(object sender, EventArgs e)
        {
            if (ContentText.Trim().Replace("\r\n", "") == "" || MessageBox.Show("Действительно удалить стикер?", "Удаление стикера", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
                Change(Sticker.ChangeAction.Delete, null);
        }

        Point pDownResize = new Point(-1, -1);
        Size szResize;
        private void imResize_MouseDown(object sender, MouseEventArgs e)
        {
            pDownResize = imResize.PointToScreen(e.Location);
            szResize = this.Size;
        }

        private void imResize_MouseMove(object sender, MouseEventArgs e)
        {
            if (pDownResize.X != -1)
            {
                Point p = imResize.PointToScreen(e.Location);
                SetNewSize(szResize.Width + p.X - pDownResize.X, szResize.Height + p.Y - pDownResize.Y);
                AdaptContestBySize(false);
            }
        }

        bool SetNewSize(int newWidth, int newHeight)
        {
            Size maxSz = stk.GetMaxSize();

            bool ret = true;
            Change(Sticker.ChangeAction.FillMaxSize, null);
            if (newWidth > maxSz.Width) { newWidth = maxSz.Width; ret = false; }
            if (newHeight > maxSz.Height) { newHeight = maxSz.Height; ret = false; }
            Width = newWidth;
            Height = newHeight;
            return ret;
        }

        private void imResize_MouseUp(object sender, MouseEventArgs e)
        {
            if (pDownResize.X != -1) Change(Sticker.ChangeAction.Resize, null);
            pDownResize.X = -1;
        }

        private void imResize_MouseLeave(object sender, EventArgs e)
        {
            if (pDownResize.X != -1) Change(Sticker.ChangeAction.Resize, null);
            pDownResize.X = -1;
        }

        public void UpdateNetStat(NetStatus stat)
        {
            if (stat == NetStatus.Local)
            {
                imNet.Image = null;
                menuMain.Items[menuMain.Items.Count - 2].Visible = true;
                menuMain.Items[menuMain.Items.Count - 1].Visible = false;
            }
            else
            {
                menuMain.Items[menuMain.Items.Count - 2].Visible = false;
                menuMain.Items[menuMain.Items.Count - 1].Visible = true;
                if (stat == NetStatus.NotActual) imNet.Image = imList.Images[1];
                if (stat == NetStatus.Write) imNet.Image = imList.Images[1];
                if (stat == NetStatus.Actual) imNet.Image = Gl.IsActualFlag ? imList.Images[0] : imList.Images[1];
            }
            imNet.Refresh();
        }

    }

    public class StickerUnit : HiderUnit
    {
        Size StickerMinSize;

        public readonly int mrgn = 6; // отстояние друг от друга и от краев экрана 
        public readonly int nearLen = 30; // размер зоны, считающейся ближайшей. На эту величину может быть подвинут стикер, если текущее его положение при переносе невозможно 
        // public readonly Size StickerMinSize = new Size(182, 160);

        public override string Ext { get { return "stick"; } }

        ToolStripMenuItem mBtnAddSticker;

        public StickerUnit(API_Funtions ExtFunc)
            : base(ExtFunc)
        {
            mBtnAddSticker = new ToolStripMenuItem("Добавить стикер");
            mBtnAddSticker.Click += new EventHandler(mBtnAddSticker_Click);

            NotifyMenuBotton = mBtnAddSticker;

            FormSticker frm = new FormSticker(new Sticker(0, "", null, NetStatus.Local, new Point(0, 0)));
            StickerMinSize = frm.MinimumSize;
            frm.Dispose();
        }

        #region *** Внешние события ******************************************

        /// <summary> событие осончания создания. Вызывается после загрузки БД. Сам модуль создается до загрузки БД </summary>
        public override void EndCreate()
        {
            RePosAllStickers();
        }

        /// <summary> событие осончания синхронизации </summary>
        public override void EndSinhronize(Gl.SinhronizeResult result)
        {
            // по окончании синхронизации с новой версией сервера, возможны новые стикеры, которые нужно отобразить
            if (result == Gl.SinhronizeResult.NEW_VERTION)
                TryShowLeftOffStickers();
            UpdateAllNetStat();
        }

        /// <summary> событие основного таймера.</summary>
        public override void TimerTick(DateTime tmrDt, DIR DB) { ;}

        /// <summary> событие изменения режима гости </summary>
        public override void GuestModeChange(bool bGuest)
        {
            List<Sticker> stkL = GetAllStickers();
            if (bGuest)
            {
                for (int i = 0; i < stkL.Count; i++)
                    stkL[i].Hide();
            }
            else
            {
                for (int i = 0; i < stkL.Count; i++)
                    stkL[i].Show();
            }
        }

        #endregion *** Внешние события ******************************************

        #region *** События меню **********************************************

        private void mBtnAddSticker_Click(object sender, EventArgs e)
        {
            AddSticker();
        }

        #endregion *** События меню **********************************************

        public override Element LoadElement(EStream st, DBItem Parent)
        {
            return new Sticker(st, (DIR)Parent);
        }

        /// <summary> Создание элемента с сервера </summary>
        public override Element CreateElementBySourceDate(string Name, DBItem Parent, string SourceData)
        {
            Sticker stk = new Sticker(0, Name, (DIR)Parent, NetStatus.NotActual, new Point(-1, -1));
            stk.SourceData = SourceData;
            return stk;
        }

        #region *** Стикеры ************************************************************************

        public void AddSticker()
        {
            // пробуем найти место для нового стикера
            Point newPos = FindPosSticker(StickerMinSize);
            // если позиция найдена
            if (newPos.X != -1)
            {
                DIR LocDir = GetLoacalDir();
                Sticker stk = new Sticker(0, LocDir.GetUnicName("Стикер", Ext), LocDir, NetStatus.Local, newPos);
                LocDir.Items.Add(stk);
                if (!stk.TryPlace(!Gl.sett.Guests)) throw new Exception("ошибка при показе стикера. Стикер создан, при отсутствии меcта на рабочем столе");
                API_Func.SaveData();
            }
            // места больше нет
            else MessageBox.Show("Невозможно добавить стикер, так как на рабочем столе уже нет места");
        }

        void UpdateAllNetStat()
        {
            DIR NetDir = GetNetDir();
            for (int i = 0; i < NetDir.Items.Count; i++)
                ((Sticker)NetDir.Items[i]).UpdateNetStat();
        }

        /// <summary> Новый стикер уместиться на указаной позиции с указанным размером. </summary>
        public bool IsFit(Point Pos, Size Sz)
        {
            return IsFit(null, Pos, Sz);
        }

        /// <summary> Стикер CheckedSticker уместиться на указаной позиции с указанным размером. </summary>
        public bool IsFit(Sticker CheckedSticker, Point Pos, Size Sz)
        {
            // проверяем границы экрана
            Rectangle disp = Screen.PrimaryScreen.WorkingArea;
            if (Pos.X < mrgn || Pos.Y < mrgn || (Pos.X + Sz.Width) > (disp.Width - mrgn) || (Pos.Y + Sz.Height) > (disp.Height - mrgn)) return false;

            List<Sticker> nl = GetAllStickers();
            // необходимый квадрат
            Rectangle r = new Rectangle(Pos.X - mrgn / 2, Pos.Y - mrgn / 2, Sz.Width + mrgn, Sz.Height + mrgn);
            // смотрим пересечения
            bool bCross = false;
            for (int m = 0; m < nl.Count; m++)
            {
                Sticker stk = nl[m];

                if (CheckedSticker != null && stk.FullPath == CheckedSticker.FullPath) continue;

                if (stk.LeftOff) continue; // не проверять непоказываемые стикеры


                StickerUnit GlobS = (StickerUnit)stk.Glob;
                Rectangle stkRect = new Rectangle(stk.Location.X - GlobS.mrgn / 2, stk.Location.Y - GlobS.mrgn / 2, stk.Size.Width + GlobS.mrgn, stk.Size.Height + GlobS.mrgn);
                if (EMath.CrossRect(r, stkRect)) { bCross = true; break; }
            }

            return !bCross;
        }

        /// <summary> Ближайшая доступная позиция для стикера в пределах установленного значения. Возвращает ближающую позицию, или -1:-1 если позиция не найдена </summary>
        public Point GetNearPos(Sticker stk, Point NeedPos, Size NeedSize)
        {
            Point Pos = NeedPos;
            Size Sz = NeedSize;

            // если запрошеная позиция удовлетворяет, возвращаем ее
            if (IsFit(stk, Pos, Sz)) return Pos;

            // корректируем относительно границ экрана
            Rectangle disp = Screen.PrimaryScreen.WorkingArea;
            if ((Pos.X + Sz.Width) > (disp.Width - mrgn)) Pos.X = disp.Width - mrgn - Sz.Width;
            if ((Pos.Y + Sz.Height) > (disp.Height - mrgn)) Pos.Y = disp.Height - mrgn - Sz.Height;
            if (Pos.X < mrgn) Pos.X = mrgn;
            if (Pos.Y < mrgn) Pos.Y = mrgn;

            // если после корректировки относительно границ, позиция удовлетворяет, возвращаем ее
            if (IsFit(stk, Pos, Sz)) return Pos;

            // пробуем найти позицию в пределах доступной зоны
            // находим все доступные позиции и выбираем позицию с наименьшим весом
            int minI = Pos.X - nearLen;
            int minJ = Pos.Y - nearLen;
            int maxI = Pos.X + nearLen;
            int maxJ = Pos.Y + nearLen;
            double minWeight = double.MaxValue;
            Point actPnt = new Point(-1, -1);
            bool bFind = false;
            for (int i = minI; i <= maxI; i++)
                for (int j = minJ; j <= maxJ; j++)
                {
                    // если позиция возможна
                    if (IsFit(stk, new Point(i, j), Sz))
                    {
                        bFind = true;
                        // вес позиции (чем более удаленный от желаемого, тем больше)
                        double actWeight = EMath.Hyp(Math.Abs(Pos.X - i), Math.Abs(Pos.Y - j));

                        if (minWeight > actWeight)
                        {
                            minWeight = actWeight;
                            actPnt = new Point(i, j);
                        }
                    }
                }

            if (bFind) return actPnt;

            return new Point(-1, -1);
        }

        /// <summary> Поиск позиции для стикера с указанными размерами </summary>
        public Point FindPosSticker(Size Sz)
        {
            // нахождение новой позиции
            int step = 10; // шаг
            Rectangle rect = Screen.PrimaryScreen.WorkingArea;

            // проверяем со сдвигом каждые 10 пикселей
            for (int i = (rect.Width - Sz.Width - mrgn * 2); i > 0; i -= step)
            {
                for (int j = 0; j < (rect.Height - Sz.Height - mrgn * 2); j += step)
                {
                    Point newPos = new Point(i, j);
                    if (IsFit(newPos, StickerMinSize))
                        return newPos;
                }
            }

            // если так ничего и не найдено, говорим, что невозможно добавить
            return new Point(-1, -1);
        }

        /// <summary>Перераспределение стикеров. (нужно при первом показу и при изменении разрешения экрана)
        /// Смотрим, стикеры, которые больше не умещаются на экране. Делаем их отложенными. 
        /// Возвращает все стикеры, которые не удалось показать </summary>
        List<Sticker> RePosAllStickers()
        {
            List<Sticker> ret = new List<Sticker>();
            List<Sticker> nl = GetAllStickers();

            // делаем всек стикеры отложенными
            for (int i = 0; i < nl.Count; i++) ((Sticker)nl[i]).LeftOff = true;
            // пытаемся вернуть их в те же места
            bool bResave = false;
            for (int i = 0; i < nl.Count; i++)
            {
                Sticker stk = nl[i];
                bool needSave;
                if (!stk.TryPlace(!Gl.sett.Guests, out needSave)) ret.Add(stk);
                if (needSave) bResave = true;
            }
            if (bResave) API_Func.SaveData();
            return ret;
        }

        /// <summary> Попытка отобразить отложенные стикеры </summary>
        public List<Sticker> TryShowLeftOffStickers()
        {
            List<Sticker> ret = new List<Sticker>();
            List<Sticker> nl = GetAllStickers();

            for (int i = 0; i < nl.Count; i++)
            {
                Sticker stk = nl[i];
                if (stk.LeftOff)
                {
                    if (!stk.TryPlace(!Gl.sett.Guests))
                        ret.Add(stk);
                }
            }

            return ret;
        }

        // преобразование стикера в заметку
        /*void StickerToNote(int ind)
        {
            Sticker stk = (Sticker)StickerDir.nl[ind];
            StickerDir.nl.RemoveAt(ind);
            if (stk.NetStat != NetStatus.Local)
            {
                Note nt = new Note(NetDir.GetUnicName(stk.NameText, RecTp.Note), stk.Text, NetDir);
                nt.NetStat = NetStatus.nNotActual;
                NetDir.nl.Add(nt);
                if (actDir.Uin == NetDir.Uin) actDir.nl.Sort();
            }
            else
            {
                Note nt = new Note(PublicDir.GetUnicName(stk.NameText, RecTp.Note), stk.Text, PublicDir);
                PublicDir.nl.Add(nt);
                if (actDir.Uin == PublicDir.Uin) actDir.nl.Sort();
            }
            stk.Close();
            RefillList();
            Gl.SaveData();
        }*/

        #endregion *** Стикеры ************************************************************************

        public List<Sticker> GetAllStickers()
        {
            List<Element> el = GetAllElements();

            List<Sticker> ret = new List<Sticker>();
            for (int i = 0; i < el.Count; i++) ret.Add((Sticker)el[i]);

            return ret;
        }

    }

    public class Sticker : Element
    {
        #region *** переменные ******************************************

        int ActVer = 0;

        protected FormSticker frm;

        public override string Ext { get { return "stick"; } }

        #region *** данные БД *********
        // текст, цвет, позиция, размер. Все берется из данных формы
        #endregion *** данные БД ******

        public enum ChangeAction { Add, Edit, Delete, Move, ColorChange, ConvertToNote, Resize, FillMaxSize, SetNet, SetLocal };

        /*public delegate void ChangeDelegate(ChangeAction actn, object prm, string Name);
        ChangeDelegate Change;*/

        public Point Location { get { return frm.Location; } }
        public Size Size { get { return frm.Size; } set { frm.Size = value; frm.AdaptContestBySize(false); } }

        public bool Visible { get { return frm.Visible; } }

        /// <summary>стикер отложеный (только, что создан, или не уместился) Такой стикер не участвует в выводе на экран и в при поиске свободного места
        /// свойство не сохраняется так, как на разных экранах может быть разное разрешение</summary>
        public bool LeftOff = true;

        public override string SourceData
        {
            get
            {
                string ret = "";
                using (MemoryStream ms = new MemoryStream())
                {
                    using (BinaryWriter bw = new BinaryWriter(ms))
                    {
                        bw.Write(ActVer);// задел на версионность
                        bw.Write(frm.ContentText);
                        bw.Write(frm.colorInd);
                        //bw.Write(frm.Location.X);
                        //bw.Write(frm.Location.Y);
                        //bw.Write(Size.Width);
                        //bw.Write(Size.Height);
                        ret = Convert.ToBase64String(ms.ToArray());
                    }
                }
                return ret;
            }
            set
            {
                using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(value)))
                {
                    using (BinaryReader br = new BinaryReader(ms))
                    {
                        br.ReadInt32(); // задел на версионность
                        string text = br.ReadString();
                        frm.colorInd = br.ReadInt32(); frm.SetColor();
                        //frm.Location = new Point(br.ReadInt32(), br.ReadInt32());
                        //Size = new Size(br.ReadInt32(), br.ReadInt32());
                        frm.ContentText = text;
                    }
                }

            }
        }

        public DateTime dtNextSaveSticker = DateTime.MaxValue;

        #endregion *** переменные *****************************************

        #region *** перегруженые фии ******************************************

        public Sticker(int ColorInd, string Name, DIR Parent, NetStatus stat, Point Pos)
            : base(Name, false, Parent, stat)
        {

            frm = new FormSticker(this);
            frm.colorInd = ColorInd;
            frm.Location = Pos;

            frm.Change = new FormSticker.ChangeDelegate(FormChange);
            frm.Construct(NetStat);

        }

        public Sticker(EStream st, DIR Parent)
            : base(st, Parent)
        {
            frm = new FormSticker(this);

            st.LoadInt(); // задел на версионность

            string text = st.LoadStr();
            frm.colorInd = st.LoadInt(); // цвет
            frm.Top = st.LoadInt(); frm.Left = st.LoadInt(); // положение
            frm.Width = st.LoadInt(); frm.Height = st.LoadInt(); // размер
            frm.ContentText = text;

            frm.Change = new FormSticker.ChangeDelegate(FormChange);
            frm.Construct(NetStat);
        }

        public override void Dispose() { frm.Close(); frm.Dispose(); }

        public override void Save(EStream st)
        {
            base.Save(st);

            st.SaveInt(ActVer); // задел на версионность

            st.SaveStr(frm.ContentText);
            st.SaveInt(frm.colorInd); // цвет
            st.SaveInt(frm.Top); st.SaveInt(frm.Left); // положение
            st.SaveInt(frm.Width); st.SaveInt(frm.Height); // размер
        }

        public override void TimerTick(DateTime tmrDt, DIR DB)
        {
            // редактирование стикера закончено (можно сохранять и синхронизировать)
            if (dtNextSaveSticker <= tmrDt)
            {
                ((StickerUnit)Glob).API_Func.SaveData();
                dtNextSaveSticker = DateTime.MaxValue;
                IsEdit = false;
                if (NetStat != NetStatus.Local) { NetStat = NetStatus.NotActual; UpdateNetStat(); }
            }
        }

        /*public override void ReWriteElement(Element newEl)
        {
            // если идет попытка переписать стикер, оставляем его старое положение и размеры
            Sticker newStk = (Sticker)newEl;

            frm.ContentText = newStk.frm.ContentText;
            frm.colorInd = newStk.frm.colorInd; frm.SetColor();

            NetStat = NetStatus.Actual;

            // если положение/размер не совпадают с новой версией
            if (newStk.frm.Location != frm.Location || newStk.Size != Size)
            {
                // ничего не делаем. Это не критично
                //NetStat = NetStatus.NotActual;
            }
        }*/

        #endregion *** перегруженые фии ******************************************

        /// <summary> Событие изменения данных с формы </summary>
        /// <param name="actn">Тип события</param>
        /// <param name="prm">Параметры</param>
        void FormChange(ChangeAction actn, object prm)
        {
            StickerUnit GlobS = (StickerUnit)Glob;
            //List<Sticker> nl = GlobS.GetAllStickers();

            // --- перемещение -----------------------
            if (actn == ChangeAction.Move)
            {
                // проверка на пересечение
                Point pos = GlobS.GetNearPos(this, frm.Location, Size);
                if (pos.X == -1)
                {
                    frm.Location = (Point)prm;
                    return;
                }
                else
                {
                    frm.Location = pos;
                }

                // попробовать отобразить отложенные стикеры, так как для них могло освободиться место
                GlobS.TryShowLeftOffStickers();
                //if (NetStat != NetStatus.Local) { NetStat = NetStatus.NotActual; UpdateNetStat(); }
                GlobS.API_Func.SaveData();
            }
            // --- редактирование -----------------------
            if (actn == ChangeAction.Edit)
            {
                IsEdit = true;
                // сохранять через 3 секунды после последнего изменения
                dtNextSaveSticker = DateTime.Now.AddSeconds(3);
                frm.UpdateNetStat(NetStatus.NotActual);
            }
            // --- добавление -----------------------
            if (actn == Sticker.ChangeAction.Add)
            {
                GlobS.AddSticker();
            }
            // --- удаление -----------------------
            if (actn == Sticker.ChangeAction.Delete)
            {
                if (NetStat != NetStatus.Local) GlobS.API_Func.AddElementInDelList(this);
                Parent.Items.Remove(this);
                Dispose();
                GlobS.API_Func.SaveData();

            }
            // --- перенос в заметку -----------------------
            /*if (actn == Sticker.ChangeAction.ConvertToNote)
            {
                // StickerToNote(StickerDir.IndexOfNote(stickerName, RecTp.Sticker));
            }*/
            // --- сделать сетевым -----------------------
            if (actn == Sticker.ChangeAction.SetNet)
            {
                NetStat = NetStatus.NotActual;
                DIR NetDir = GlobS.GetNetDir();
                if (!Parent.Items.Remove(this)) throw new Exception("Ошибка при перемещении стикера. Стикер не найден в локальном каталоге");
                Parent = NetDir;
                Name = NetDir.GetUnicName(Name, Ext);
                NetDir.Items.Add(this);
                UpdateNetStat();
                GlobS.API_Func.SaveData();
            }
            // --- сделать локльным -----------------------
            if (actn == Sticker.ChangeAction.SetLocal)
            {
                GlobS.API_Func.AddElementInDelList(this);

                NetStat = NetStatus.Local;
                DIR LocDir = GlobS.GetLoacalDir();
                if (!Parent.Items.Remove(this)) throw new Exception("Ошибка при перемещении стикера. Стикер не найден в сетевом каталоге");
                Parent = LocDir;
                Name = LocDir.GetUnicName(Name, Ext);
                LocDir.Items.Add(this);
                UpdateNetStat();

                GlobS.API_Func.SaveData();
            }
            // --- изменение размеров -----------------------
            if (actn == Sticker.ChangeAction.Resize)
            {
                // попробовать отобразить отложенные стикеры, так как для них могло освободиться место
                GlobS.TryShowLeftOffStickers();
                GlobS.API_Func.SaveData();
            }
            // --- изменение цвета -----------------------
            if (actn == Sticker.ChangeAction.ColorChange)
            {
                if (NetStat != NetStatus.Local) { NetStat = NetStatus.NotActual; UpdateNetStat(); }
                GlobS.API_Func.SaveData();
            }

        }

        public void UpdateNetStat() { frm.UpdateNetStat(NetStat); }

        public void Show() { if (!LeftOff) frm.Show(); }
        public void Hide() { frm.Hide(); }

        public void Close() { frm.Close(); frm.Dispose(); }

        public Size GetMaxSize()
        {
            Rectangle maxRectW = new Rectangle(frm.Left, frm.Top, Screen.PrimaryScreen.WorkingArea.Width - frm.Left, frm.Height);
            Rectangle maxRectH = new Rectangle(frm.Left, frm.Top, frm.Width, Screen.PrimaryScreen.WorkingArea.Height - frm.Top);

            List<Sticker> stkL = ((StickerUnit)Glob).GetAllStickers();

            for (int i = 0; i < stkL.Count; i++)
            {
                Sticker stk = stkL[i];

                if (stk.FullPath == FullPath) continue; // если это текущий стикер, пропускаем его
                if (stk.LeftOff) continue; // если стикер отложеный, пропускаем его

                Rectangle r = stk.frm.RectangleToScreen(stk.frm.ClientRectangle);

                if (EMath.CrossRect(maxRectW, r)) maxRectW.Width = r.Left - 1 - maxRectW.X;
                if (EMath.CrossRect(maxRectH, r)) maxRectH.Height = r.Top - 1 - maxRectH.Y;

            }

            return new Size(maxRectW.Width, maxRectH.Height);
        }

        public void SetMinSize()
        {
            frm.Size = frm.MinimumSize;
            frm.AdaptContestBySize(false);
        }

        /// <summary>Попытка найти место для отложенного стикера и показать его. Если стоит bShow стикер показывается на столе </summary>
        public bool TryPlace(bool bShow)
        {
            bool NeedSave;
            return TryPlace(bShow, out NeedSave);
        }

        /// <summary>Попытка найти место для отложенного стикера и расположить его там </summary>
        public bool TryPlace(bool bShow, out bool NeedSave)
        {
            if (!LeftOff) throw new Exception("Попытка найти новое место для стикера, который уже показан");

            StickerUnit GlobS = (StickerUnit)Glob;
            Rectangle disp = Screen.PrimaryScreen.WorkingArea;

            // Если стикер умещается, со своими заданными размерами и позицией, просто показываем
            if (GlobS.IsFit(frm.Location, Size))
            {
                LeftOff = false;
                if (bShow) frm.Show();
                NeedSave = false;
                return true;
            }

            // Иначе: Смотрим, уместится ли, где-то еще на экране
            Point newPnt = GlobS.FindPosSticker(Size);
            if (newPnt.X != -1)
            {
                LeftOff = false;
                frm.Location = newPnt;
                if (bShow) frm.Show();
                if (NetStat != NetStatus.Local) NetStat = NetStatus.NotActual;
                NeedSave = true;
                return true;
            }

            // Смотрим, умещается ли с минимальным размером
            if (GlobS.IsFit(frm.Location, frm.MinimumSize))
            {
                LeftOff = false;
                SetMinSize();
                if (bShow) frm.Show();
                if (NetStat != NetStatus.Local) NetStat = NetStatus.NotActual;
                NeedSave = true;
                return true;
            }

            // Если нет, Смотрим, уместится ли где-то еще на экране с минимальным размером
            newPnt = GlobS.FindPosSticker(frm.MinimumSize);
            if (newPnt.X != -1)
            {
                LeftOff = false;
                frm.Location = newPnt;
                SetMinSize();
                if (bShow) frm.Show();
                if (NetStat != NetStatus.Local) NetStat = NetStatus.NotActual;
                NeedSave = true;
                return true;
            }

            // Если нет, оставляем отложеным
            NeedSave = false;
            return false;

        }

    }

}
